Hey ! I stumbled across your post while searching myself, since I found a solution, let me post it here.
Goal
To recap, I am trying to read several audio files and output them in different channels. In my setup, I have 2 devices that both have two channels so I have 4 channels in the end. I use an aggregate device and run the iOS app with Catalyst (I couldn't make aggregate device work with AVAudioEngine on macOS 🤷♂️).
I use AVAudioSourceNode as input but it should also work with AVAudioPlayerNode since both have an auAudioUnit property.
Setup formats
When the engine is being setup, I make sure that the channels count of the output node is 4:
let outputFormat = audioEngine.outputNode.outputFormat(forBus: 0)
print("Channels count:", outputFormat.channelCount) // 4
If not, this might be because the AVAudioSession is not configured with the multiRoute category.
Mixer -> Output
Then, I had to connect the engine main mixer node to the output node using the output node format.
audioEngine.connect(audioEngine.mainMixerNode, to: audioEngine.outputNode, format: format)
I don't know why it's necessary. When I read the doc, it seems that it's already done by default.
If the client never sets the connection format between the mainMixerNode and the outputNode, the engine always updates the format to track the format of the outputNode on startup or restart, even after an AVAudioEngineConfigurationChange.
Source -> Mixer
I also had to connect each source node to the main mixer node using the output format. Otherwise the channel mapping will not be effective.
engine.connect(sourceNode, to: engine.mainMixerNode, format: outputFormat)
Source channels mapping
In the end, I could set the auAudioUnit.channelMap of each source node to output to the desired channels. For instance
sourceNode.auAudioUnit.channelMap = [0, 1, -1, -1]
to output in the first pair of speakers (default) and
sourceNode.auAudioUnit.channelMap = [-1, -1, 0, 1]
to output to the second pair of speakers.